using System;
using System.Collections;
using System.Text;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace LANamp
{
    public class PlayList
    {
        private ArrayList playlist;
        private int unusablesRemoved;
        private MusicInfo selectedItem = null;
        private frmIndexing indexForm;
        public delegate void NewEntriesEvent(PlayList addplaylist);
        public delegate void UpdatedEvent();
        public TimeSpan timeNeeded;
        public event NewEntriesEvent NewEntries;
        public event UpdatedEvent Updated;
        public PlayList()
        {
            playlist = new ArrayList();
        }
        public int Count
        {
            get
            {
                lock (playlist)
                {
                    return playlist.Count;
                }
            }
        }

        public TimeSpan Length
        {
            get{
                TimeSpan length = new TimeSpan();
                lock(playlist)
                {
                    foreach (MusicInfo song in playlist)
                    {
                        length += song.Length;
                    }
                }
                return length;
            }
        }

        public ArrayList Items
        {
            get
            {
                lock (playlist)
                {
                    return playlist;
                }
            }
        }
        public PlayList Clone()
        {
            lock (playlist)
            {
                PlayList tmp = new PlayList();
                tmp.playlist = (ArrayList)playlist.Clone();
                return tmp;
            }
        }
        public MusicInfo SelectedItem
        {
            get
            {
                return selectedItem;
            }
        }
        public void Add(MusicInfo song)
        {
            lock (playlist)
            {
                playlist.Add(song);
                selectedItem = song;
            }
            DoUpdate();
        }
        public void Insert(MusicInfo song)
        {
            lock (playlist)
            {
                playlist.Insert(0,song);
                selectedItem = song;
            }
            DoUpdate();
        }

        public void Remove(MusicInfo song)
        {
            lock (playlist)
            {
                int index = playlist.IndexOf(song);
                if ((index > -1) && (index < playlist.Count))
                {
                    playlist.RemoveAt(index);
                    if (playlist.Count > 0)
                        selectedItem = (MusicInfo)((index < playlist.Count) ? playlist[index] : playlist[index - 1]);
                    else
                        selectedItem = null;
                }
            }
            DoUpdate();
        }

        public void RemoveAt(int index)
        {
            lock (playlist)
            {
                if ((index > -1) && (index < playlist.Count))
                {
                    playlist.RemoveAt(index);
                    if (playlist.Count > 0)
                        selectedItem = (MusicInfo)((index < playlist.Count) ? playlist[index] : playlist[index - 1]);
                    else
                        selectedItem = null;
                }
            }
            DoUpdate();
        }

        public void Add(PlayList addplaylist)
        {
            lock (playlist)
            {
                playlist.AddRange((ICollection)addplaylist.playlist);
            }
            this.unusablesRemoved += addplaylist.unusablesRemoved;
            this.Sort();
            if (NewEntries != null) NewEntries(addplaylist);
            DoUpdate();
        }

        public void Sort()
        {
            lock (playlist)
            {
                playlist.Sort();
            }
        }

        public void MoveUp(int index)
        {
            lock (playlist)
            {
                if (index > 0)
                {
                    MusicInfo song = (MusicInfo)playlist[index];
                    if (song != null)
                    {
                        playlist.Insert(index - 1, song);
                        playlist.RemoveAt(index + 1);
                        selectedItem = song;
                    }
                }
            }
            DoUpdate();
        }

        public void MoveDown(int index)
        {
            lock (playlist)
            {
                if ((index > -1) && (index < (playlist.Count - 1)))
                {
                    MusicInfo song = (MusicInfo)playlist[index];
                    if (song != null)
                    {
                        playlist.RemoveAt(index);
                        playlist.Insert(index+1, song);
                        selectedItem = song;
                    }
                }
            }
            DoUpdate();
        }

        public void Clear()
        {
            playlist.Clear();
            selectedItem = null;
            DoUpdate();
        }

        public void IndexFolder(string path, bool recursive)
        {
            indexForm = new frmIndexing();
            if (recursive)
            {
                indexForm.labStatus.Text = "Starting thread...";
                indexForm.Show();
                Thread indexer = new Thread(new ParameterizedThreadStart(indexfolder_threaded));
                indexer.Name = "FileIndexer";
                indexer.IsBackground = true;
                indexer.Start(path);
            }
            else
            {
                indexForm.labStatus.Text = "Indexing files...";
                indexForm.Show();
                indexfolder_simple(path);
            }
        }

        public void IndexFolder(string path)
        {
            IndexFolder(path, true);
        }

        public void UpdateStatus(string newStatus)
        {
            object[] args = new object[1];
            args[0] = newStatus.Clone();
            indexForm.BeginInvoke(new frmIndexing.UpdateStatusEvent(indexForm.UpdateStatus), args);
        }

        public void UpdateLocation(string newLocation)
        {
            object[] args = new object[1];
            args[0] = newLocation.Clone();
            indexForm.BeginInvoke(new frmIndexing.UpdateLocationEvent(indexForm.UpdateLocation), args);
        }

        public void UpdateFolder(string newFolder)
        {
            object[] args = new object[1];
            args[0] = newFolder.Clone();
            indexForm.BeginInvoke(new frmIndexing.UpdateFolderEvent(indexForm.UpdateFolder), args);
        }

        public void SetProgress(int progress)
        {
            object[] args = new object[1];
            args[0] = progress;
            indexForm.Invoke(new frmIndexing.SetProgresEvent(indexForm.SetProgress), args);
        }

        public void SetTotalProgress(int progress)
        {
            object[] args = new object[1];
            args[0] = progress;
            indexForm.Invoke(new frmIndexing.SetProgresEvent(indexForm.SetTotalProgress), args);
        }

        public void ResetProgress(int maxValue)
        {
            object[] args = new object[1];
            args[0] = maxValue;
            indexForm.BeginInvoke(new frmIndexing.ResetProgressEvent(indexForm.ResetProgress), args);
        }

        public void MarqueeProgress()
        {
            indexForm.BeginInvoke(new frmIndexing.MarqueeProgressEvent(indexForm.MarqueeProgress));
        }

        private void indexfolder_threaded(object opath)
        {
            string pathlist = (string)opath;
            PlayList newlist = new PlayList();
            foreach (string path in pathlist.Split(new char[] {';'}))
            {
                indexfolder(path, newlist, true);
            }
            indexForm.Invoke(new MethodInvoker(indexForm.Close));
        }

        private void indexfolder_simple(string path)
        {
            PlayList newlist = new PlayList();
            indexfolder(path, newlist, false);
            indexForm.Close();
        }

        private void indexfolder(string path, PlayList newlist, bool recursive)
        {
            indexfolder(path, newlist, recursive, true);
        }

        private void indexfolder(string path, PlayList newlist, bool recursive, bool firstlevel)
        {
            DateTime start = DateTime.Now;
            SetTotalProgress(0);
            UpdateLocation("Indexing folder:");
            UpdateFolder(path);
            UpdateStatus("Indexing files...");
            DirectoryInfo dir = new DirectoryInfo(path);
            try
            {
                if (recursive)
                {
                    foreach (DirectoryInfo subdir in dir.GetDirectories())
                    {
                        indexfolder(subdir.FullName, newlist, recursive, false);
                        Thread.Sleep(5);
                    }

                }
                foreach (FileInfo file in dir.GetFiles("*.mp3"))
                {
                    newlist.Add(new MusicInfo(file.FullName));
                }
                if (firstlevel)
                {
                    SetTotalProgress(10);
                    UpdateStatus("Reading ID3 tags...");
                    UpdateLocation("Folder:");
                    UpdateFolder(path);
                    string lastPath = path;
                    int i = 1;
                    int total = newlist.Items.Count;
                    ResetProgress(total);
                    ArrayList unusableItems = new ArrayList(50);
                    foreach (MusicInfo song in newlist.Items)
                    {
                        SetProgress(i);
                        SetTotalProgress(10 + (int)(170 * ((double)i / (double)total)));
                        path = (new FileInfo(song.Filepath)).DirectoryName;
                        if (path != lastPath)
                        {
                            lastPath = path;
                            UpdateFolder(path);
                        }
                        song.ReadTag();
                        if (song.Unusable) unusableItems.Add(song);
                        i++;
                    }
                    newlist.unusablesRemoved = unusableItems.Count;
                    total = unusableItems.Count;
                    SetTotalProgress(180);
                    UpdateStatus("Removing unusable files (" + total + ")...");
                    UpdateLocation("File:");
                    i = 1;
                    ResetProgress(total);
                    foreach (MusicInfo unusableItem in unusableItems)
                    {
                        SetProgress(i);
                        SetTotalProgress(180 + (int)(10 * ((double)i / (double)total)));
                        UpdateFolder(unusableItem.Filepath);
                        newlist.Remove(unusableItem);
                        i++;
                    }
                    unusableItems = null;
                    MarqueeProgress();
                    newlist.playlist.Sort();
                    SetTotalProgress(190);
                    UpdateStatus("Merging new songs in existing list...");
                    UpdateLocation("");
                    UpdateFolder("");
                    this.Add(newlist);
                    SetTotalProgress(195);
                    UpdateStatus("Communicating new items...");
                    SetTotalProgress(20);
                    UpdateStatus("Done");

                    DateTime end = DateTime.Now;
                    Console.WriteLine("Total files read:      " + (newlist.Count + newlist.unusablesRemoved));
                    Console.WriteLine("Total files unusable:  " + newlist.unusablesRemoved);
                    Console.WriteLine("Total files added:     " + newlist.Count);
                    Console.WriteLine("Total running time:    " + (end - start));
                    Console.WriteLine("Average time per file: " + new TimeSpan((end - start).Ticks / (newlist.Count+newlist.unusablesRemoved)));
                    Console.WriteLine("Total playlist length: " + Length.ToString());
                    this.timeNeeded = (end - start);
                }
                DoUpdate();
            }
            catch { }
            if (firstlevel)
            {
            }
        }

        public MusicInfo GetNext()
        {
            MusicInfo result = null;
            lock (playlist)
            {
                if (playlist.Count > 0)
                {
                    MusicInfo next = (MusicInfo)playlist[0];
                    playlist.Remove(next);
                    result = next;
                    if (next == selectedItem)
                        if (playlist.Count > 0) selectedItem = (MusicInfo)playlist[0];
                }
            }
            DoUpdate();
            return result;
        }

        public MusicInfo GetRandom()
        {
            lock (playlist)
            {
                if (playlist.Count > 0)
                {
                    Random rnd = new Random();
                    MusicInfo next = (MusicInfo)playlist[rnd.Next(playlist.Count)];
                    return next;
                }
                else
                {
                    return null;
                }
            }
        }

        private void DoUpdate()
        {
            if (Updated != null) Updated();
        }
    }
}
